﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualBasic.PowerPacks;
using MergulhoGrafo;
using MergulhoGrafo.QuadriParticao;

namespace Viewer
{
    public partial class Form1 : Form
    {
        ShapeContainer _Canvas = new ShapeContainer();
        ShapeContainer _Canvas_Exemplo2 = new ShapeContainer();
        ShapeContainer _Canvas_Exemplo3 = new ShapeContainer();
        ShapeContainer _Canvas_Exemplo4 = new ShapeContainer();
        Point center = new Point(310, 310);
        int raio = 300;

        public Form1()
        {
            InitializeComponent();
            this.ConstruirAbaExemplo1();
            this.ConstruirAbaExemplo2();
            this.ConstruirAbaExemplo3();
            this.ConstruirAbaExemplo4();
        }

        private void ConstruirAbaExemplo4()
        {
            GrafoRetangulo grafo = new GrafoRetangulo("../../entrada.txt");
            QuadriParticao quadriParticao = new QuadriParticao(grafo, "../../entrada.txt");
            ParticaoMl particaoMl = new ParticaoMl(grafo, quadriParticao);

            particaoMl.MatrizPosicoesCentroDisco(grafo, quadriParticao);

            _Canvas_Exemplo4.Parent = pnlSec;

            List<Tuple<string, double>> Borda = particaoMl.PosicaoPontosBorda();

            List<Coordenadas> todasPosicoes = new List<Coordenadas>();

            int angulo = 0;

            foreach (Tuple<string, double> tupla in Borda)
            {
                string[] split_coord1 = tupla.Item1.Split(' ');
                int index_aresta_coord1 = Convert.ToInt32(split_coord1[0]);

                if (particaoMl.Poligono.Contains(index_aresta_coord1))
                {
                    todasPosicoes.Add(PlotPointInAngle2(tupla, _Canvas_Exemplo4, angulo++));
                    if (todasPosicoes.Count > 1)
                        PlotEdge(new Edge("", todasPosicoes.Last(), todasPosicoes[todasPosicoes.Count - 2]), Color.Black, _Canvas_Exemplo4);

                    PlotCircle(todasPosicoes.Last(), particaoMl.SetorAngulo, particaoMl.ListaAnguloOrientavel);
                }
            }
            PlotEdge(new Edge("", todasPosicoes.Last(), todasPosicoes[0]), Color.Black, _Canvas_Exemplo4);
        }

        private void PlotCircle(Coordenadas coordenadas, List<Tuple<int, int>> list, List<Tuple<int, bool>> list2)
        {
            Tuple<int, int> setor_angulo = list.Find(tupla => tupla.Item2 == Convert.ToInt32(coordenadas.indexVertice));
            Tuple<int, bool> angulo_orienta = list2.Find(tupla => tupla.Item1 == Convert.ToInt32(coordenadas.indexVertice));

            OvalShape point = new OvalShape();
            point.Parent = _Canvas_Exemplo4;
            point.Location = new Point((int)coordenadas.x - 50, (int)coordenadas.y - 50);
            point.Size = new Size(2 * 50, 2 * 50);
            point.FillStyle = FillStyle.Solid;
            if (angulo_orienta.Item2)
            {
                point.FillColor = Color.Transparent;
            }
            else
            {

                point.FillColor = Color.LightGray;
            }
            
            Label txtConjuntos = new Label();
            txtConjuntos.Parent = pnlSec;
            txtConjuntos.Location = point.Location;
            txtConjuntos.AutoSize = true;
            txtConjuntos.BringToFront();

            txtConjuntos.Text = "Setor - " + setor_angulo.Item1;
             
        }

        private void ConstruirAbaExemplo3()
        {
            GrafoRetangulo grafo = new GrafoRetangulo("../../entrada2.txt");
            QuadriParticao quadriParticao = new QuadriParticao(grafo, "../../entrada2.txt");
            ParticaoMl particaoMl = new ParticaoMl(grafo, quadriParticao);

            this.ImprimirQuadriParticao(quadriParticao, this.pnlConjuntosEx3, particaoMl);

            particaoMl.MatrizPosicoesCentroDisco(grafo, quadriParticao);

            _Canvas_Exemplo3.Parent = pnlExemplo3;

            OvalShape point = new OvalShape();
            point.Parent = _Canvas_Exemplo3;
            point.Location = new Point(center.X - raio, center.Y - raio);
            point.Size = new Size(2 * raio, 2 * raio);
            point.FillStyle = FillStyle.Solid;
            point.FillColor = Color.Transparent;

            //PlotPointInAngle(new Tuple<int, double>(0, Math.PI / 2));

            List<Tuple<string, double>> Borda = particaoMl.PosicaoPontosBorda();

            List<Coordenadas> todasPosicoes = new List<Coordenadas>();

            foreach (Tuple<string, double> tupla in Borda)
            {
                todasPosicoes.Add(PlotPointInAngle(tupla, _Canvas_Exemplo3));
            }

            this.ArestasSetores(todasPosicoes, _Canvas_Exemplo3, particaoMl.Poligono, Borda);

            Tuple<double[,], List<List<string>>> MatrizMiolo = particaoMl.MatrizPosicoesCentroDisco(grafo, quadriParticao);

            this.CalcularIterativamenteTodasPosicoes(MatrizMiolo, todasPosicoes);

            List<Edge> todasArestas = this.GerarAresta(MatrizMiolo, todasPosicoes, grafo);

            //List<Edge> todasArestasGrafo = this.GerarGrafo(MatrizMiolo, grafo);

            foreach (Edge edge in todasArestas)
            {
                PlotEdge(edge, Color.Red, _Canvas_Exemplo3);
            }
        }

        private void ConstruirAbaExemplo2()
        {
            GrafoRetangulo grafo = new GrafoRetangulo("../../entrada2.txt");
            QuadriParticao quadriParticao = new QuadriParticao(grafo, "../../entrada2.txt", true);
            ParticaoMl particaoMl = new ParticaoMl(grafo, quadriParticao);

            this.ImprimirQuadriParticao(quadriParticao, this.pnlConjuntosEx2, particaoMl);

            particaoMl.MatrizPosicoesCentroDisco(grafo, quadriParticao);

            _Canvas_Exemplo2.Parent = pnlExemplo2;

            OvalShape point = new OvalShape();
            point.Parent = _Canvas_Exemplo2;
            point.Location = new Point(center.X - raio, center.Y - raio);
            point.Size = new Size(2 * raio, 2 * raio);
            point.FillStyle = FillStyle.Solid;
            point.FillColor = Color.Transparent;

            //PlotPointInAngle(new Tuple<int, double>(0, Math.PI / 2));

            List<Tuple<string, double>> Borda = particaoMl.PosicaoPontosBorda();

            List<Coordenadas> todasPosicoes = new List<Coordenadas>();

            foreach (Tuple<string, double> tupla in Borda)
            {
                todasPosicoes.Add(PlotPointInAngle(tupla, _Canvas_Exemplo2));
            }

            this.ArestasSetores(todasPosicoes, _Canvas_Exemplo2, particaoMl.Poligono, Borda);
            
            Tuple<double[,], List<List<string>>> MatrizMiolo = particaoMl.MatrizPosicoesCentroDisco(grafo, quadriParticao);

            this.CalcularIterativamenteTodasPosicoes(MatrizMiolo, todasPosicoes);

            List<Edge> todasArestas = this.GerarAresta(MatrizMiolo, todasPosicoes, grafo);

            //List<Edge> todasArestasGrafo = this.GerarGrafo(MatrizMiolo, grafo);

            foreach (Edge edge in todasArestas)
            {
                PlotEdge(edge, Color.Red, _Canvas_Exemplo2);
            }
        }

        private void ArestasSetores(List<Coordenadas> todasPosicoes, ShapeContainer _Canvas_Exemplo2, List<int> Poligono, List<Tuple<string, double>> Borda)
        {
            double posicaoAtual = 0;
            double incremento = Math.PI * 2 / Poligono.Count;

            while (Math.Round(posicaoAtual, 3) < Math.Round(Math.PI, 3))
            {
                Tuple<string, double> a = Borda.Find(ponto => Math.Round(ponto.Item2, 3) == Math.Round(posicaoAtual, 3));
                Tuple<string, double> b = Borda.Find(ponto => Math.Round(ponto.Item2, 3) == Math.Round(posicaoAtual + Math.PI, 3));

                Coordenadas coord_a = todasPosicoes.Find(posicao => posicao.indexVertice.Equals(a.Item1));
                Coordenadas coord_b = todasPosicoes.Find(posicao => posicao.indexVertice.Equals(b.Item1));

                PlotEdge(new Edge("", coord_a, coord_b), Color.Blue, _Canvas_Exemplo2);

                posicaoAtual += incremento;
            }

            /*
            foreach (Coordenadas coord1 in todasPosicoes)
            {
                string[] split_coord1 = coord1.indexVertice.Split(' ');
                int index_aresta_coord1 = Convert.ToInt32(split_coord1[0]);

                if (Poligono.Contains(index_aresta_coord1) && split_coord1[1].Equals("a"))
                {
                    foreach (Coordenadas coord2 in todasPosicoes)
                    {
                        string[] split_coord2 = coord2.indexVertice.Split(' ');
                        int index_aresta_coord2 = Convert.ToInt32(split_coord2[0]);

                        if (index_aresta_coord2 == index_aresta_coord1 && split_coord2[1].Equals("c"))
                            PlotEdge(new Edge("", coord1, coord2), Color.Blue, _Canvas_Exemplo2);
                    }
                }
            }
             */
        }

        private void ImprimirQuadriParticao(QuadriParticao quadriParticao, Panel panel, ParticaoMl particaoMl)
        {
            Label txtConjuntos = new Label();
            txtConjuntos.Parent = panel;
            txtConjuntos.Location = new Point(20, 30);
            txtConjuntos.AutoSize = true;

            txtConjuntos.Text = "Conjunto A - " + quadriParticao.particaoA.ToString();

            txtConjuntos = new Label();
            txtConjuntos.Parent = panel;
            txtConjuntos.Location = new Point(20, 55);
            txtConjuntos.AutoSize = true;

            txtConjuntos.Text = "Conjunto B - " + quadriParticao.particaoB.ToString();

            txtConjuntos = new Label();
            txtConjuntos.Parent = panel;
            txtConjuntos.Location = new Point(20, 80);
            txtConjuntos.AutoSize = true;

            txtConjuntos.Text = "Conjunto C - " + quadriParticao.particaoC.ToString();

            txtConjuntos = new Label();
            txtConjuntos.Parent = panel;
            txtConjuntos.Location = new Point(20, 105);
            txtConjuntos.AutoSize = true;

            txtConjuntos.Text = "Conjunto D - " + quadriParticao.particaoD.ToString();

            txtConjuntos = new Label();
            txtConjuntos.Parent = panel;
            txtConjuntos.Location = new Point(20, 130);
            txtConjuntos.AutoSize = true;

            txtConjuntos.Text = "Conjunto T - " + quadriParticao.particaoC.ToStringT();
            
            Console.WriteLine("Conjunto A");
            Console.WriteLine(quadriParticao.particaoA.ToString());
            Console.WriteLine("Conjunto B");
            Console.WriteLine(quadriParticao.particaoB.ToString());
            Console.WriteLine("Conjunto C");
            Console.WriteLine(quadriParticao.particaoC.ToString());
            Console.WriteLine("Conjunto D");
            Console.WriteLine(quadriParticao.particaoD.ToString());
            Console.WriteLine("Conjunto T");
            Console.WriteLine(quadriParticao.particaoC.ToStringT());

            int incremento = 15;

            foreach (Tuple<int,int> item in particaoMl.SetorAngulo)
            {
                txtConjuntos = new Label();
                txtConjuntos.Parent = panel;
                txtConjuntos.Location = new Point(20, 160 + incremento);
                txtConjuntos.AutoSize = true;

                txtConjuntos.Text = "S - " + item.Item1 + " - A - " + item.Item2;

                incremento += 15;
            }
        }
        
        private void ConstruirAbaExemplo1()
        {

            GrafoRetangulo grafo = new GrafoRetangulo("../../entrada.txt");
            QuadriParticao quadriParticao = new QuadriParticao(grafo, "../../entrada.txt");
            ParticaoMl particaoMl = new ParticaoMl(grafo, quadriParticao);
            this.ImprimirQuadriParticao(quadriParticao, this.pnlConjuntosEx1, particaoMl);
            particaoMl.MatrizPosicoesCentroDisco(grafo, quadriParticao);

            _Canvas.Parent = panel1;

            OvalShape point = new OvalShape();
            point.Parent = _Canvas;
            point.Location = new Point(center.X - raio, center.Y - raio);
            point.Size = new Size(2 * raio, 2 * raio);
            point.FillStyle = FillStyle.Solid;
            point.FillColor = Color.Transparent;

            //PlotPointInAngle(new Tuple<int, double>(0, Math.PI / 2));

            List<Tuple<string, double>> Borda = particaoMl.PosicaoPontosBorda();

            List<Coordenadas> todasPosicoes = new List<Coordenadas>();

            foreach (Tuple<string, double> tupla in Borda)
            {
                todasPosicoes.Add(PlotPointInAngle(tupla, _Canvas));
            }

            foreach (Coordenadas coord1 in todasPosicoes)
            {
                foreach (Coordenadas coord2 in todasPosicoes)
                {
                    if (particaoMl.Poligono.Contains(Convert.ToInt32(coord1.indexVertice[0].ToString())) &&
                        coord1.indexVertice[0] == coord2.indexVertice[0])
                        PlotEdge(new Edge("", coord1, coord2), Color.Blue, _Canvas);
                }
            }

            Tuple<double[,], List<List<string>>> MatrizMiolo = particaoMl.MatrizPosicoesCentroDisco(grafo, quadriParticao);

            this.CalcularIterativamenteTodasPosicoes(MatrizMiolo, todasPosicoes);

            List<Edge> todasArestas = this.GerarAresta(MatrizMiolo, todasPosicoes, grafo);

            //List<Edge> todasArestasGrafo = this.GerarGrafo(MatrizMiolo, grafo);

            foreach (Edge edge in todasArestas)
            {
                PlotEdge(edge, Color.Red, _Canvas);
            }
        }

        private List<Edge> GerarGrafo(Tuple<double[,], List<List<string>>> MatrizMiolo, List<Coordenadas> todasPosicoes, GrafoRetangulo grafo)
        {
            List<Edge> retorno = new List<Edge>();
            List<Coordenadas> todasPontos = new List<Coordenadas>();

            #region Criando Vértices
            for (int i = 1; i <= grafo.numeroVertices; i++)
            {
                double x, y;
                if (i == 1)
                {
                    x = -1;
                    y = 1;
                }
                else if (i == 2)
                {
                    x = 1;
                    y = 1;
                }
                else if (i == 3)
                {
                    x = 2;
                    y = 0;
                }
                else if (i == 4)
                {
                    x = 1;
                    y = -1;
                }
                else if (i == 5)
                {
                    x = -1;
                    y = -1;
                }
                else if (i == 6)
                {
                    x = -2;
                    y = 0;
                }
                else
                {
                    x = 0;
                    y = 0;
                }
                Coordenadas c = new Coordenadas(i + "", x, y);
                todasPontos.Add(c);
            }
            #endregion

            #region Vertices Bs

            #endregion
            #region Gerar Arestas



            #endregion

            return retorno;
        }

        private List<Edge> GerarAresta(Tuple<double[,], List<List<string>>> MatrizMiolo, List<Coordenadas> todasPosicoes, GrafoRetangulo grafo)
        {
            List<Edge> retorno = new List<Edge>();
            int numeroVertices = MatrizMiolo.Item2.Count;
            double[,] A = MatrizMiolo.Item1;

            //Criando as arestas de dentro do disco
            for (int i = 1; i <= numeroVertices; i++)
            {
                for (int j = i+1; j <= numeroVertices; j++)
                {
                    if (A[i-1, j-1] == -1)
                    {
                        Retangulo ret = grafo.Retangulos.Find(r => (r.Vert1 == i && r.Vert2 == j) ||
                            (r.Vert2 == i && r.Vert1 == j));

                        Coordenadas v1 = todasPosicoes.Find(v => v.indexVertice.Equals(i + ""));
                        Coordenadas v2 = todasPosicoes.Find(v => v.indexVertice.Equals(j + ""));

                        Edge newEdge = new Edge(ret.numeroRef + "", v1, v2);
                        retorno.Add(newEdge);
                    }
                }
            }

            //Criando as aresta entre o centro e a borda
            int k = 1;
            foreach (List<string> list_vert_b in MatrizMiolo.Item2)
            {
                foreach (string vert_b in list_vert_b)
                {
                    Coordenadas v1 = todasPosicoes.Find(v => v.indexVertice.Equals(k + ""));
                    Coordenadas v2 = todasPosicoes.Find(v => v.indexVertice.Equals(vert_b));
                    Edge newEdge = new Edge(vert_b, v1, v2);
                    retorno.Add(newEdge);
                }
                k++;
            }

            return retorno;
        }

        private void CalcularIterativamenteTodasPosicoes(Tuple<double[,], List<List<string>>> MatrizMiolo, List<Coordenadas> todasPosicoes)
        {
            int numeroVertices = MatrizMiolo.Item2.Count;

            double[,] bs = this.MatrizB(MatrizMiolo.Item2, todasPosicoes);
            double[,] posicoes = new double[numeroVertices, 2];
            double[,] A = MatrizMiolo.Item1;

            double maiorVariacao = 0;
            double limiar = 0.000000000000001;

            do
            {
                maiorVariacao = 0;
                //Tem que ver o menorVariacao
                double[,] Somatorio = this.Somatorio(A, posicoes, numeroVertices);
                for (int i = 0; i < numeroVertices; i++)
                {
                    double temp = posicoes[i, 0];
                    // X
                    posicoes[i, 0] = (bs[i, 0] - Somatorio[i, 0]) / A[i, i];

                    // Y
                    posicoes[i, 1] = (bs[i, 1] - Somatorio[i, 1]) / A[i, i];

                    temp -= posicoes[i, 0];

                    if (Math.Abs(temp) > maiorVariacao)
                    {
                        maiorVariacao = Math.Abs(temp);
                    }
                }
            }
            while (maiorVariacao > limiar);

            for (int i = 0; i < numeroVertices; i++)
            {
                Coordenadas coord_atual = new Coordenadas((i+1)+"",posicoes[i,0],posicoes[i,1]);
                todasPosicoes.Add(coord_atual);
            }
        }

        private double[,] Somatorio(double[,] A, double[,] posicoes, int tamanho)
        {
            double[,] retorno = new double[tamanho, 2];

            for (int i = 0; i < tamanho; i++)
            {
                for (int j = 0; j < tamanho; j++)
                {
                    if (i != j)
                    {
                        retorno[i, 0] += A[i, j] * posicoes[j, 0];
                        retorno[i, 1] += A[i, j] * posicoes[j, 1];
                    }
                }
            }

            return retorno;
        }

        private double[,] MatrizB(List<List<string>> list, List<Coordenadas> todasPosicoes)
        {
            double[,] retorno = new double[list.Count, 2];
            int i = 0;

            foreach (List<string> item in list)
            {
                foreach (string b in item)
                {
                    Coordenadas posicao = todasPosicoes.Find(coord => coord.indexVertice.Equals(b));
                    retorno[i, 0] += posicao.x;
                    retorno[i, 1] += posicao.y;
                }
                i++;
            }

            return retorno;
        }

        private void PlotEdge(Edge edge, Color color, ShapeContainer parent)
        {
            LineShape line = new LineShape();

            line.StartPoint = new Point((int)edge.v1.x + 2, (int)edge.v1.y - 2);
            line.EndPoint = new Point((int)edge.v2.x + 2, (int)edge.v2.y - 2);
            line.BorderWidth = 1;
            line.BorderStyle = System.Drawing.Drawing2D.DashStyle.Dash;
            line.BorderColor = color;
            line.Parent = parent;
            line.BringToFront();

            Label lbl = new Label();
            lbl.Text = edge.nome_aresta;
            lbl.AutoSize = true;
            lbl.Location = new Point((int)((edge.v1.x + edge.v2.x) / 2), (int)((edge.v1.y + edge.v2.y) / 2));
            lbl.Parent = parent;
        }

        private Coordenadas PlotPointInAngle(Tuple<string, double> tupla, ShapeContainer parent)
        {
            int y = (int)(center.Y + raio * Math.Cos(tupla.Item2)) - 2;
            int x = (int)(center.X + raio * Math.Sin(tupla.Item2)) - 2;

            Coordenadas retorno = new Coordenadas(tupla.Item1, x, y);

            OvalShape point = new OvalShape();
            point.Parent = parent;
            point.Location = new Point(x, y);
            point.Size = new Size(5, 5);
            point.FillStyle = FillStyle.Solid;
            point.FillColor = Color.Red;
            point.SelectionColor = Color.Red;

            Label lbl = new Label();
            lbl.Text = tupla.Item1.Split(' ')[0] + " b";
            lbl.AutoSize = true;
            lbl.Location = new Point(x + 5, y + 5);
            lbl.Parent = parent;

            return retorno;
        }

        private Coordenadas PlotPointInAngle2(Tuple<string, double> tupla, ShapeContainer parent, int angulo)
        {
            int y = (int)(center.Y + (raio - 100) * Math.Cos(tupla.Item2)) - 2;
            int x = (int)(center.X + (raio - 100) * Math.Sin(tupla.Item2)) - 2;

            Coordenadas retorno = new Coordenadas(angulo+"", x, y);

            OvalShape point = new OvalShape();
            point.Parent = parent;
            point.Location = new Point(x, y);
            point.Size = new Size(5, 5);
            point.FillStyle = FillStyle.Solid;
            point.FillColor = Color.Red;
            point.SelectionColor = Color.Red;

            Label lbl = new Label();
            lbl.Text = angulo.ToString();
            lbl.AutoSize = true;
            lbl.Location = new Point(x + 5, y + 5);
            lbl.Parent = parent;

            return retorno;
        }
    }
}
